package com.such.kit.datastructure.graph.bean;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import com.such.kit.validate.ValidateCommons;

/**
 * @作者 SUCH
 * @日期 2016-9-27 下午2:32:31
 * @描述 （图）顶点
 * @param <T>
 */
public class Vertex<T> implements Serializable {

	private static final long serialVersionUID = 8423099049458815265L;

	/** label：顶点标识 */
	private T label;
	/** inEdges：从邻接点到当前顶点的边，既入度边 */
	private List<Edge<T>> inEdges = new LinkedList<Edge<T>>();
	/** outEdges：从当前顶点到邻接点的边，既出度边 */
	private List<Edge<T>> outEdges = new LinkedList<Edge<T>>();

	// 算法实现辅助属性
	/** visited：顶点是否已被访问 */
	private boolean visited = false;
	/** inEdgeCount：入度数 */
	private int inEdgeCount = 0;
	/** outEdgeCount：出度数 */
	private int outEdgeCount = 0;
	/** predecessorVertex：在一条深度线中的前置顶点 */
	private Vertex<T> predecessorVertex = null;

	public Vertex(T label){
		this.label = label;
	}

	public T getLabel() {
		return this.label;
	}

	public List<Edge<T>> getInEdges() {
		return this.inEdges;
	}

	public List<Edge<T>> getOutEdges() {
		return this.outEdges;
	}

	/**
	 * <p>方法名：hasPredecessor</p>
	 * <p>描述：是否有前置顶点</p>
	 * @return boolean
	 */
	public boolean hasPredecessor() {
		return !this.inEdges.isEmpty();
	}

	/**
	 * <p>方法名：hasNeighbor</p>
	 * <p>描述：是否有临接点</p>
	 * @return boolean
	 */
	public boolean hasNeighbor() {
		return !this.outEdges.isEmpty();
	}

	public boolean isVisited() {
		return this.visited;
	}

	public void setVisited(boolean visited) {
		this.visited = visited;
	}

	public int getInEdgeCount() {
		return this.inEdgeCount;
	}

	public void setInEdgeCount(int inEdgeCount) {
		this.inEdgeCount = inEdgeCount;
	}

	public int getOutEdgeCount() {
		return this.outEdgeCount;
	}

	public void setOutEdgeCount(int outEdgeCount) {
		this.outEdgeCount = outEdgeCount;
	}

	public Vertex<T> getPredecessorVertex() {
		return this.predecessorVertex;
	}

	public void setPredecessorVertex(Vertex<T> predecessorVertex) {
		this.predecessorVertex = predecessorVertex;
	}

	/**
	 * <p>方法名：connectMe</p>
	 * <p>描述：连接目标顶点与当前顶点，连接后则形成入度边</p>
	 * @param targetVertex 目标顶点
	 * @return 是否连接成功
	 */
	public boolean connectMe(Vertex<T> targetVertex) {
		return connectMe(targetVertex, 0);
	}

	/**
	 * <p>方法名：connectMe</p>
	 * <p>描述：连接目标顶点与当前顶点，连接后则形成入度边</p>
	 * @param targetVertex 目标顶点
	 * @param edgeWeight 边的权重
	 * @return 是否连接成功（失败原因为边重复）
	 */
	public boolean connectMe(Vertex<T> targetVertex, double cost) {
		boolean result = false;
		if (ValidateCommons.isNotEmpty(targetVertex)) {
			boolean duplicateEdge = false;
			// 检查边是否重复
			for (Edge<T> edge : this.inEdges) {
				Vertex<T> checkVertex = edge.getTargetVertex();
				if (targetVertex.equals(checkVertex)) {
					duplicateEdge = true;
					break;
				}
			}
			if (!duplicateEdge) {
				this.inEdges.add(new Edge<T>(targetVertex, cost));
				result = true;
			}
		}
		return result;
	}

	/**
	 * <p>方法名：disconnectMe</p>
	 * <p>描述：断开目标顶点与当前顶点，删除入度边</p>
	 * @param targetVertex 目标顶点
	 */
	public void disconnectMe(Vertex<T> targetVertex) {
		if (ValidateCommons.isNotEmpty(targetVertex)) {
			Iterator<Edge<T>> it = this.inEdges.iterator();
			while (it.hasNext()) {
				Edge<T> edge = it.next();
				Vertex<T> checkVertex = edge.getTargetVertex();
				if (targetVertex.equals(checkVertex)) {
					it.remove();
				}
			}
		}
	}

	/**
	 * <p>方法名：connectNeighbor</p>
	 * <p>描述：连接当前顶点与目标顶点，连接后则形成出度边</p>
	 * @param targetVertex 目标顶点
	 * @return 是否连接成功
	 */
	public boolean connectNeighbor(Vertex<T> targetVertex) {
		return connectNeighbor(targetVertex, 0);
	}

	/**
	 * <p>方法名：connectNeighbor</p>
	 * <p>描述：连接当前顶点与目标顶点，连接后则形成出度边</p>
	 * @param targetVertex 目标顶点
	 * @param edgeWeight 边的权重
	 * @return 是否连接成功（失败原因为边重复）
	 */
	public boolean connectNeighbor(Vertex<T> targetVertex, double cost) {
		boolean result = false;
		if (ValidateCommons.isNotEmpty(targetVertex)) {
			boolean duplicateEdge = false;
			// 检查边是否重复
			for (Edge<T> edge : this.outEdges) {
				Vertex<T> checkVertex = edge.getTargetVertex();
				if (targetVertex.equals(checkVertex)) {
					duplicateEdge = true;
					break;
				}
			}
			if (!duplicateEdge) {
				this.outEdges.add(new Edge<T>(targetVertex, cost));
				result = true;
			}
		}
		return result;
	}

	/**
	 * <p>方法名：disconnectNeighbor</p>
	 * <p>描述：断开当前顶点与目标顶点，删除出度边</p>
	 * @param targetVertex 目标顶点
	 */
	public void disconnectNeighbor(Vertex<T> targetVertex) {
		if (ValidateCommons.isNotEmpty(targetVertex)) {
			Iterator<Edge<T>> it = this.outEdges.iterator();
			while (it.hasNext()) {
				Edge<T> edge = it.next();
				Vertex<T> checkVertex = edge.getTargetVertex();
				if (targetVertex.equals(checkVertex)) {
					it.remove();
				}
			}
		}
	}

	/**
	 * <p>方法名：getNextUnvisitedNeighbor</p>
	 * <p>描述：获取下一个未访问过的临接点，并将其设置为已访问</p>
	 * @return 下一个未访问过的临接点
	 */
	public Vertex<T> getNextUnvisitedNeighbor() {
		for (Edge<T> edge : this.outEdges) {
			Vertex<T> targetVertex = edge.getTargetVertex();
			if(!targetVertex.isVisited()) {
				targetVertex.setVisited(true);
				return targetVertex;
			}
		}
		return null;
	}

	/**
	 * <p>方法名：getUnvisitedNeighbors</p>
	 * <p>描述：获取未访问过的临接点，并将其设置为已访问</p>
	 * @return 未访问过的临接点
	 */
	public List<Vertex<T>> getUnvisitedNeighbors() {
		List<Vertex<T>> unvisitedNeighbors = new ArrayList<Vertex<T>>();
		for (Edge<T> edge : this.outEdges) {
			Vertex<T> targetVertex = edge.getTargetVertex();
			if(!targetVertex.isVisited()) {
				targetVertex.setVisited(true);
				unvisitedNeighbors.add(targetVertex);
			}
		}
		return unvisitedNeighbors;
	}

	/**
	 * <p>方法名：equals</p>
	 * <p>描述：比较二个顶点是否相等</p>
	 * @param otherVertex
	 * @return boolean
	 */
	public boolean equals(Vertex<T> otherVertex){
		if (ValidateCommons.isEmpty(otherVertex)) {
			return false;
		} else if (this.label.equals(otherVertex.getLabel())) {
			return true;
		}
		return false;
	}

	/**
	 * <p>方法名：reset</p>
	 * <p>描述：重置顶点算法实现辅助属性</p>
	 */
	public void reset() {
		setVisited(false);
		setInEdgeCount(this.inEdges.size());
		setOutEdgeCount(this.outEdges.size());
		setPredecessorVertex(null);
	}

}